class State:
    """
        A representation of the state in the domination game 
    """

    # TODO: extract the tile size from the settings or something
    TILE_SIZE = 16
    REGION1 = """
5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
5 u u u u u u u t t t v w w w w w x y y y y B B B B B C C C 5
5 u u u u u u c t t t v v u u u x x y y y y A A A D D C C C 5
5 u u u u c c c t t t v v u u u x x y y y y A A A D D C C C 5
5 u u b b c c c t t t v v u u u x x y y y y A z D D D C C C 5
5 b b b b c c c 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 z D D D C C C 5
5 b b b 5 c c c 5 n n n n h s s p p y y y g g z E E 5 C C C 5
5 b b b 5 H H d 5 m m h h h s s p p y y y g g F E E 5 C C C 5
5 a a a 5 H H d 5 m m h h 5 5 5 5 5 l l r r 5 F E E 5 C C C 5
5 T T T 5 H H d i i f f f k k q q l l l r r 5 F E E 5 C C C 5
5 T T T 5 H H e i i f f f k k q q l o o o o 5 G G I 5 C C C 5
5 T T T U U R e 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 G G I I C C C 5
5 S S T U U R e P Q Q O O M M J J J L L N G G G G I I C C C 5
5 S S S U U R R P Q Q O O M M J J J L L N N G G I I I C C C 5
5 S S S S U R R P Q Q O O M M J J J L L N P P I I I I C C C 5
5 S S S S S S R P P Q Q O M K K K K K L N P I I I I I C C C 5
5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
"""
    # 1 = red spawn point
    # 2 = blue spawn point
    # 3 = ammo
    # 4 = cp
    # 5 = wall



    def __init__(self, team, max_turn):
        """ Create a default state
        needs max_turn as input to enable discretized angles in 2pi/max_turn
        regions """
        self.team = team
        self.max_turn = max_turn


        # instantiate variables with standard values
        self.agent_info = []
        self.winning = 0
        self.cp1 = 0
        self.cp2 = 0

        self.regions = [[t.upper() for t in l.split()] for l in self.REGION1.strip().split('\n')]
        self.field_width = len(self.regions[0]) * self.TILE_SIZE
        self.field_height = len(self.regions) * self.TILE_SIZE


    def first_update(self, agent, observation):
        """ sets the stuff we all agents know """
        self.winning = 0 if (observation.score[0] ==
            observation.score[1]) else int(observation.score[0]
            > observation.score[1])
        self.cp1 = observation.cps[0][2]
        self.cp2 = observation.cps[1][2]

        # reset the agent info
        self.agent_info = []

        # and update the rest normally
        self.update(agent, observation)

    def update(self, agent, observation):
        """ Adds information from one agent to this state representation.
        This is mainly the compilin' of the agent_info list """

        # find in which region the location is
        region = self.regions[observation.loc[1]/
                self.TILE_SIZE][observation.loc[0]/self.TILE_SIZE]

        # discretize the angle from location (this should work, no?)
        angle = self.discretize_angle(observation.angle)#int(observation.angle/self.max_turn)

        # find the number of observed enemies:
        foes_observed = len(observation.foes)

        # check if the agent can shoot at least one of them:
        can_shoot_enemy = int(agent.determine_shoot()[2])

        # agent_status the ammo amount if alive, and -1 if agent is dead.
        agent_status = observation.ammo if observation.respawn_in > 0 else -1

        # the previous action
        agent_previous_action = observation.previous_action

        # put em all in the awesome list
        self.agent_info.append([region, angle, foes_observed,
            can_shoot_enemy, agent_status, agent_previous_action])


    def get_hash(self):
        """ returns the string that represents the state 
        The hash values are seperated by '_' so we can easily read a
        state from a hash string. """
        # note that we could also just use the tuple as key for the dict

        if self.team == TEAM_BLUE:
            self.agent_info = list(reversed(self.agent_info))

        # first add the agent info list
        s = "_".join(map(lambda x: str(x),
            [item for sublist in self.agent_info for item in sublist]))

        # second add the other information:
        s += "_%s_%s_%s" % (str(self.cp1), str(self.cp2), str(self.winning))
        return s

    def get_state_from_hash(self):
        """ returns the state from a hash string """
        # TODO: damnit, wanted to use scanf, but python does not have this?!
        pass

    def discretize_angle(self, angle):
        """
        Angle should be from 0 to 2pi
        Returns a 1, 2, 3 or 4 depending on the angel
        """
        part = math.pi/2
        start = math.pi/4
        # .25 pi to .75 pi
        if angle > start and angle < start + part:
            return 0
        start += part
        # .75 pi to 1.25 pi
        if angle > start and angle < start + part:
            return 1
        start += part
        # 1.25 pi to 1.75 pi
        if angle > start and angle < start + part:
            return 2
        start += part
        # 1.75 pi to .25 pi
        if angle > start or angle < math.pi/4:
            return 3
        return -1

    def convert_location(self, x, y):
        """ Converts a location to the same location on the other side of
        the map """
        return (self.field_width - x, self.field_height - y)
